package cn.jiedanba.itext5.rsa.signHash;

import cn.jiedanba.itext5.BCProvider;
import cn.jiedanba.itext5.gm.sign.vo.GetPdfHash;
import cn.jiedanba.itext5.gm.sign.vo.GetPdfHashParamVo;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.*;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import org.bouncycastle.asn1.*;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.cert.Certificate;

/**
 * IText pdf分离式签名 . 场景：从外部设备获取p1数据，例如ukey，签验签服务器，KMS系统
 *
 * @author dell
 */
public class ITextSignHashUtil extends BCProvider {
    /**
     * 创建签名域，并获取摘要值
     *
     * @param param
     */
    public static GetPdfHash getPdfHash(GetPdfHashParamVo param) {
        try {
            ByteArrayOutputStream tmpos = new ByteArrayOutputStream();
            PdfReader reader = new PdfReader(param.getPdf());
            PdfStamper stamper = PdfStamper.createSignature(reader, tmpos, '\0', null, true);
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
            appearance.setVisibleSignature(
                    new Rectangle(param.getLlx(), param.getLly(), param.getUrx(), param.getUry()), param.getPageNo(),
                    appearance.getNewSigName());
            appearance.setSignatureGraphic(Image.getInstance(param.getSignImage()));
            appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
            appearance.setRenderingMode(RenderingMode.GRAPHIC);
            appearance.setReason(param.getReason());
            appearance.setLocation(param.getLocation());
            ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE,
                    PdfName.ADBE_PKCS7_DETACHED);
            MakeSignature.signExternalContainer(appearance, external, new Integer(8192 * 2 + 2));
            InputStream data = appearance.getRangeStream();

            MessageDigest externalDigest = MessageDigest.getInstance(param.getHashAlgorithm(), BC);
            byte digest[] = DigestAlgorithms.digest(data, externalDigest);

            ASN1EncodableVector attribute = new ASN1EncodableVector();
            ASN1EncodableVector v = new ASN1EncodableVector();
            v.add(new ASN1ObjectIdentifier(SecurityIDs.ID_CONTENT_TYPE));
            v.add(new DERSet(new ASN1ObjectIdentifier(SecurityIDs.ID_PKCS7_DATA)));
            attribute.add(new DERSequence(v));
            v = new ASN1EncodableVector();
            v.add(new ASN1ObjectIdentifier(SecurityIDs.ID_MESSAGE_DIGEST));
            v.add(new DERSet(new DEROctetString(digest)));
            attribute.add(new DERSequence(v));

            byte[] sh = new DERSet(attribute).getEncoded(ASN1Encoding.DER);
            GetPdfHash returnVo = new GetPdfHash();
            returnVo.setSignHash(sh);
            returnVo.setDigesHash(digest);
            returnVo.setEmptySignaturePdf(tmpos.toByteArray());
            returnVo.setFieldName(appearance.getFieldName());
            return returnVo;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 签署p7
     *
     * @param digest        pdf原文摘要值
     * @param p1            私钥签名数据
     * @param chain         证书链
     * @param hashAlgorithm 摘要算法
     * @param tsaClient     时间戳
     * @return
     * @throws GeneralSecurityException
     */
    public static byte[] signHash(byte[] digest, byte[] p1, Certificate[] chain, String hashAlgorithm,
                                  TSAClient tsaClient) throws GeneralSecurityException {
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, null, false);
        sgn.setExternalDigest(p1, null, "RSA");
        return sgn.getEncodedPKCS7(digest, tsaClient, null, null, CryptoStandard.CMS);
    }
}
